using System;
using System.Collections;
using System.Collections.Generic;

namespace EntityKit.Runtime
{
    /// <summary>
    /// 可观察列表类，当列表内容发生变化时会触发相应的事件
    /// </summary>
    /// <typeparam name="T">列表元素的类型</typeparam>
    public class BindableList<T> : IEnumerable<T>, IEnumerable
    {
        private List<T> mList = new List<T>();
        
        /// <summary>
        /// 当向列表中添加元素时触发的事件
        /// </summary>
        public event Action<T> OnItemAdded;
        
        /// <summary>
        /// 当从列表中移除元素时触发的事件
        /// </summary>
        public event Action<T> OnItemRemoved;
        
        /// <summary>
        /// 当列表中某个位置的元素被替换时触发的事件
        /// </summary>
        public event Action<T, T> OnItemReplaced; // oldValue, newValue
        
        /// <summary>
        /// 当列表被清空时触发的事件
        /// </summary>
        public event Action OnCleared;
        
        /// <summary>
        /// 当列表内容发生任何变化时触发的事件
        /// </summary>
        public event Action OnChanged;
        
        /// <summary>
        /// 获取列表中元素的数量
        /// </summary>
        public int Count => mList.Count;
        
        /// <summary>
        /// 获取或设置指定索引处的元素
        /// </summary>
        /// <param name="index">元素的索引</param>
        /// <returns>指定索引处的元素</returns>
        public T this[int index]
        {
            get => mList[index];
            set
            {
                var oldValue = mList[index];
                mList[index] = value;
                OnItemReplaced?.Invoke(oldValue, value);
                OnChanged?.Invoke();
            }
        }
        
        /// <summary>
        /// 向列表末尾添加元素
        /// </summary>
        /// <param name="item">要添加的元素</param>
        public void Add(T item)
        {
            mList.Add(item);
            OnItemAdded?.Invoke(item);
            OnChanged?.Invoke();
        }
        
        /// <summary>
        /// 向列表末尾添加多个元素
        /// </summary>
        /// <param name="items">要添加的元素集合</param>
        public void AddRange(IEnumerable<T> items)
        {
            if (items == null) return;
            
            foreach (var item in items)
            {
                mList.Add(item);
                OnItemAdded?.Invoke(item);
            }
            OnChanged?.Invoke();
        }
        
        /// <summary>
        /// 移除列表中第一个匹配的元素
        /// </summary>
        /// <param name="item">要移除的元素</param>
        /// <returns>如果成功移除了元素，则为true；否则为false</returns>
        public bool Remove(T item)
        {
            bool removed = mList.Remove(item);
            if (removed)
            {
                OnItemRemoved?.Invoke(item);
                OnChanged?.Invoke();
            }
            return removed;
        }
        
        /// <summary>
        /// 移除指定索引处的元素
        /// </summary>
        /// <param name="index">要移除元素的索引</param>
        public void RemoveAt(int index)
        {
            if (index >= 0 && index < mList.Count)
            {
                T item = mList[index];
                mList.RemoveAt(index);
                OnItemRemoved?.Invoke(item);
                OnChanged?.Invoke();
            }
        }
        
        /// <summary>
        /// 将元素插入到列表中的指定索引处
        /// </summary>
        /// <param name="index">插入位置的索引</param>
        /// <param name="item">要插入的元素</param>
        public void Insert(int index, T item)
        {
            if (index >= 0 && index <= mList.Count)
            {
                mList.Insert(index, item);
                OnItemAdded?.Invoke(item);
                OnChanged?.Invoke();
            }
        }
        
        /// <summary>
        /// 清空列表中的所有元素
        /// </summary>
        public void Clear()
        {
            mList.Clear();
            OnCleared?.Invoke();
            OnChanged?.Invoke();
        }
        
        /// <summary>
        /// 确定列表中是否包含特定元素
        /// </summary>
        /// <param name="item">要在列表中定位的元素</param>
        /// <returns>如果在列表中找到item，则为true；否则为false</returns>
        public bool Contains(T item)
        {
            return mList.Contains(item);
        }
        
        /// <summary>
        /// 搜索指定元素并返回整个列表中第一个匹配项的从零开始的索引
        /// </summary>
        /// <param name="item">要在列表中定位的对象</param>
        /// <returns>如果找到，则为item的第一个匹配项的从零开始的索引；否则为-1</returns>
        public int IndexOf(T item)
        {
            return mList.IndexOf(item);
        }
        
        /// <summary>
        /// 将列表中的元素复制到一个数组中
        /// </summary>
        /// <param name="array">复制到的目标数组</param>
        /// <param name="arrayIndex">从目标数组的指定索引处开始复制</param>
        public void CopyTo(T[] array, int arrayIndex)
        {
            mList.CopyTo(array, arrayIndex);
        }
        
        /// <summary>
        /// 获取一个枚举器，用于遍历列表
        /// </summary>
        /// <returns>可用于遍历列表的枚举器</returns>
        public IEnumerator<T> GetEnumerator()
        {
            return mList.GetEnumerator();
        }
        
        /// <summary>
        /// 获取一个枚举器，用于遍历列表
        /// </summary>
        /// <returns>可用于遍历列表的枚举器</returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
        
        /// <summary>
        /// 返回列表中所有元素的字符串表示形式
        /// </summary>
        /// <returns>列表的字符串表示形式</returns>
        public override string ToString()
        {
            return $"BindableList<{typeof(T).Name}> (Count: {Count})";
        }
    }
}